#include "SAW.h"

#include <cstring>

SAW::SAW(unsigned long* stepSizes, size_t amountStepSizes, unsigned int seed) {
    this->directions = 4;
    this->stepSizes = stepSizes;
    this->amountStepSizes = amountStepSizes;
    this->possibleDirections = new bool[directions];
    srand(seed);
}

unsigned long SAW::newStepSize() {
    unsigned long result = stepSizes[stepSizeCounter];
    stepSizeCounter = (stepSizeCounter + 1) % amountStepSizes;
    return result;
}

long* SAW::walk() {
    stepSizeCounter = 0;
    stepSize = newStepSize();
    long oldX = startX;
    long oldY = startY;
    map[oldX][oldY] = true;
    long newX, newY;
    unsigned long steps = 0;
    minX = startX;
    maxX = startX;
    minY = startY;
    maxY = startY;

    unsigned long direction;
    unsigned long directions = calculatePossibleDirections(oldX, oldY);
    while (directions != 0) {
        direction = getDirection((rand() % directions));

        if (direction == 0) {
            newX = oldX;
            newY = oldY + stepSize;
        } else if (direction == 1) {
            newX = oldX + stepSize;
            newY = oldY;
        } else if (direction == 2) {
            newX = oldX;
            newY = oldY - stepSize;
        } else if (direction == 3) {
            newX = oldX - stepSize;
            newY = oldY;
        }

		++steps;
		if(newX < minX){
			minX = newX;
		} else if(newX > maxX){
			maxX = newX;
		}
		if(newY < minY){
			minY = newY;
		} else if(newY > maxY){
			maxY = newY;
		}
        doStep(oldX, oldY, newX, newY);
        oldX = newX;
        oldY = newY;
        directions = calculatePossibleDirections(oldX, oldY);
    }
	map.clear();
	
	long* result = new long[5];
	result[0] = minX;
	result[1] = maxX;
	result[2] = minY;
	result[3] = maxY;
	result[4] = steps;
	
    return result;
}

void SAW::doStep(long oldX, long oldY, long newX, long newY) {
    if (oldX == newX) {
        if (oldY < newY) {
            for (long i = oldY + 1; i <= newY; ++i) {
                map[oldX][i] = true;
            }
        } else {
            for (long i = newY; i < oldY; ++i) {
                map[oldX][i] = true;
            }
        }
    } else {
        if (oldX < newX) {
            for (long i = oldX + 1; i <= newX; ++i) {
                map[i][oldY] = true;
            }
        } else {
            for (long i = newX; i < oldX; ++i) {
                map[i][oldY] = true;
            }
        }
    }
    stepSize = newStepSize();
}

bool SAW::canGoNord(long x, long y) {
    for (long i = 1; i <= stepSize; ++i) {
        if (map[x][y + i]) {
            return false;
        }
    }
    return true;
}

bool SAW::canGoEast(long x, long y) {
    for (long i = 1; i <= stepSize; ++i) {
        if (map[x + i][y]) {
            return false;
        }
    }
    return true;
}

bool SAW::canGoSouth(long x, long y) {
    for (long i = 1; i <= stepSize; ++i) {
        if (map[x][y - i]) {
            return false;
        }
    }
    return true;
}

bool SAW::canGoWest(long x, long y) {
    for (long i = 1; i <= stepSize; ++i) {
        if (map[x - i][y]) {
            return false;
        }
    }
    return true;
}

unsigned long SAW::calculatePossibleDirections(long x, long y) {
    unsigned long counter = 0;

    possibleDirections[0] = canGoNord(x, y);
    possibleDirections[1] = canGoEast(x, y);
    possibleDirections[2] = canGoSouth(x, y);
    possibleDirections[3] = canGoWest(x, y);

    for (long i = 0; i < directions; ++i) {
        if (possibleDirections[i]) {
            ++counter;
        }
    }

    return counter;
}

unsigned long SAW::getDirection(unsigned int number) {
    size_t counter = 0;

    for (size_t i = 0; i < directions; ++i) {
        if (number == counter && possibleDirections[i]) {
            return i;
        }
        if (possibleDirections[i]) {
            ++counter;
        }
    }
}
